package com.benyanyi.loglib;

import android.text.TextUtils;

import androidx.annotation.NonNull;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * @author myLove
 * @time 2017/10/31 10:39
 * @e-mail ben@yanyi.red
 * @overview
 */

public final class Jlog {
    static final String LINE_SEPARATOR = System.getProperty("line.separator");

    private static final String DEFAULT_MESSAGE = "********";
    private static final String PARAM = "Param";
    private static final String NULL = "null";

    static final int JSON_INDENT = 4;

    static boolean mIsGlobalTagEmpty = true;
    static boolean IS_INIT = true;
    static LogConfig mConfig = new LogConfig();

    public static LogConfig init() {
        return mConfig;
    }

    public static void init(LogConfig config) {
        if (config == null) {
            return;
        }
        mConfig = config;
    }

    public static void v() {
        printLog(LogType.V, null, DEFAULT_MESSAGE);
    }

    public static void v(Object msg) {
        printLog(LogType.V, null, msg);
    }

    static void v(int length, Object msg) {
        printLog(LogType.V, null, msg);
    }

    public static void v(Object tag, @NonNull Object... objects) {
        printLog(LogType.V, tag, objects);
    }

    public static void d() {
        printLog(LogType.D, null, DEFAULT_MESSAGE);
    }

    public static void d(Object msg) {
        printLog(LogType.D, null, msg);
    }

    public static void d(Object tag, Object... objects) {
        printLog(LogType.D, tag, objects);
    }

    public static void i() {
        printLog(LogType.I, null, DEFAULT_MESSAGE);
    }

    public static void i(Object msg) {
        printLog(LogType.I, null, msg);
    }

    public static void i(Object tag, Object... objects) {
        printLog(LogType.I, tag, objects);
    }

    public static void w() {
        printLog(LogType.W, null, DEFAULT_MESSAGE);
    }

    public static void w(Object msg) {
        printLog(LogType.W, null, msg);
    }

    public static void w(Object tag, Object... objects) {
        printLog(LogType.W, tag, objects);
    }

    public static void e() {
        printLog(LogType.E, null, DEFAULT_MESSAGE);
    }

    public static void e(Object msg) {
        printLog(LogType.E, null, msg);
    }

    public static void e(Object tag, Object... objects) {
        printLog(LogType.E, tag, objects);
    }

    public static void a() {
        printLog(LogType.A, null, DEFAULT_MESSAGE);
    }

    public static void a(Object msg) {
        printLog(LogType.A, null, msg);
    }

    public static void a(Object tag, Object... objects) {
        printLog(LogType.A, tag, objects);
    }

    public static void json(Object jsonFormat) {
        printLog(LogType.JSON, null, jsonFormat);
    }

    public static void json(Object tag, String jsonFormat) {
        printLog(LogType.JSON, tag, jsonFormat);
    }

    public static void xml(Object xml) {
        printLog(LogType.XML, null, xml);
    }

    public static void xml(Object tag, String xml) {
        printLog(LogType.XML, tag, xml);
    }

    public static void file(File targetDirectory, Object msg) {
        printFile(null, targetDirectory, null, msg);
    }

    public static void file(Object tag, File targetDirectory, Object msg) {
        printFile(tag, targetDirectory, null, msg);
    }

    public static void file(Object tag, File targetDirectory, String fileName, Object msg) {
        printFile(tag, targetDirectory, fileName, msg);
    }

    public static void debug() {
        printDebug(null, DEFAULT_MESSAGE);
    }

    public static void debug(Object msg) {
        printDebug(null, msg);
    }

    public static void debug(Object tag, Object... objects) {
        printDebug(tag, objects);
    }

    public static void trace() {
        printStackTrace();
    }

    public static void errorWriteFile(Throwable throwable) {
        if (mConfig.isWrite()) {
            BaseLog.writeError(null, throwable);
        }
    }

    public static void errorWriteFile(String tag, Throwable throwable) {
        if (mConfig.isWrite()) {
            BaseLog.writeError(tag, throwable);
        }
    }

    private static void printStackTrace() {
        if (!IS_INIT && !mConfig.isWrite()) {
            return;
        }
        if (!mConfig.isShowLog() && isApkInDebug() && !mConfig.isWrite()) {
            return;
        }

        Throwable tr = new Throwable();
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        tr.printStackTrace(pw);
        pw.flush();
        String message = sw.toString();

        String[] traceString = message.split("\\n\\t");
        StringBuilder sb = new StringBuilder();
        sb.append("\n");
        for (String trace : traceString) {
            if (trace.contains("at Jlog")) {
                continue;
            }
            sb.append(trace).append("\n");
        }
        String[] contents = wrapperContent(null, sb.toString());
        if (contents == null) {
            return;
        }
        String tag = contents[0];
        String msg = contents[1];
        String headString = contents[2];
        BaseLog.printDefault(LogType.D, tag, headString + msg);
    }

    private static void printLog(LogType type, Object tagStr, Object... objects) {
        if (!IS_INIT && !mConfig.isWrite()) {
            return;
        }
        if (!mConfig.isShowLog() && isApkInDebug() && !mConfig.isWrite()) {
            return;
        }
        String[] contents = wrapperContent(tagStr, objects);
        if (contents == null) {
            return;
        }
        String tag = contents[0];
        String msg = contents[1];
        String headString = contents[2];

        switch (type) {
            default:
            case V:
            case D:
            case I:
            case W:
            case E:
            case A:
                BaseLog.printDefault(type, tag, headString + msg);
                break;
            case JSON:
                JsonLog.printJson(tag, msg, headString);
                break;
            case XML:
                XmlLog.printXml(tag, msg, headString);
                break;
        }
    }

    private static void printDebug(Object tagStr, Object... objects) {
        if (!IS_INIT || !mConfig.isWrite()) {
            return;
        }
        String[] contents = wrapperContent(tagStr, objects);
        if (contents == null) {
            return;
        }
        String tag = contents[0];
        String msg = contents[1];
        String headString = contents[2];
        BaseLog.printDefault(LogType.D, tag, headString + msg);
    }

    private static void printFile(Object tagStr, File targetDirectory, String fileName, Object objectMsg) {
        if (!IS_INIT && !mConfig.isWrite()) {
            return;
        }
        if (!mConfig.isShowLog() && isApkInDebug() && !mConfig.isWrite()) {
            return;
        }
        String[] contents = wrapperContent(tagStr, objectMsg);
        if (contents == null) {
            return;
        }
        String tag = contents[0];
        String msg = contents[1];
        String headString = contents[2];

        FileLog.printFile(tag, targetDirectory, fileName, headString, msg);
    }

    private static String[] wrapperContent(Object tagStr, Object... objects) {

        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        String fileName = "";
        StackTraceElement targetElement = null;
        for (StackTraceElement element : stackTrace) {
            String name = element.getFileName();
            String packageName = element.getClass().getPackage().getName();
            if (!"VMStack.java".equals(name) && !"Thread.java".equals(name) && !"Jlog.java".equals(name)
                    && !"KtUtils.kt".equals(name)) {
                fileName = name;
                targetElement = element;
                break;
            }
        }
        if (targetElement == null) {
            return null;
        }
        String str = "$";
        if (fileName.contains(str)) {
            fileName = fileName.split("\\$")[0];
        }
        String methodName = targetElement.getMethodName();
        int lineNumber = targetElement.getLineNumber();

        if (lineNumber < 0) {
            lineNumber = 0;
        }

        String tag = (tagStr == null ? fileName : tagStr).toString();

        if (mIsGlobalTagEmpty && TextUtils.isEmpty(tag)) {
            tag = mConfig.getTagDefault();
        } else if (!mIsGlobalTagEmpty) {
            tag = mConfig.getTagDefault();
        }

        String msg = (objects == null) ? mConfig.getNullTip() : getObjectsString(objects);
        String headString = "[ (" + fileName + ":" + lineNumber + ")#" + methodName + " ] ";

        return new String[]{tag, msg, headString};
    }

    private static String getObjectsString(Object... objects) {

        if (objects.length > 1) {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("\n");
            for (int i = 0; i < objects.length; i++) {
                Object object = objects[i];
                if (object == null) {
                    stringBuilder.append(PARAM).append("[").append(i).append("]").append(" = ").append(NULL).append("\n");
                } else {
                    stringBuilder.append(PARAM).append("[").append(i).append("]").append(" = ").append(object).append("\n");
                }
            }
            return stringBuilder.toString();
        } else {
            Object object = objects[0];
            return object == null ? NULL : object.toString();
        }
    }

    private static boolean isApkInDebug() {
        return BuildConfig.DEBUG;
//        try {
//            @SuppressLint("PrivateApi")
//            Class<?> activityThread = Class.forName("android.app.ActivityThread");
//            Object thread = activityThread.getMethod("currentActivityThread").invoke(null);
//            Object app = activityThread.getMethod("getApplication").invoke(thread);
//            Application application;
//            if (app == null) {
//                application = null;
//            } else {
//                application = (Application) app;
//            }
//            ApplicationInfo info = application.getApplicationInfo();
//            return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0;
//        } catch (Exception e) {
//            return false;
//        }
    }

    void aaa(String... str){

    }

}
